从循环到函数式:Racket 常用高阶函数详解
在很多传统语言(如 C、Python、Java)中,我们习惯写:
for/while循环- 手动遍历元素
- 手动维护索引
acc = []这样不断追加数据- 写许多条件分支来处理状态
而在 Racket 中,你会看到一种完全不同的风格:
同样的任务,用 map、filter、apply 等高阶函数轻松完成。
无需循环,也无需变量控制状态。
为什么会这样?
这种写法又好在哪里?
本文会从“思维方式的变化”开始,再详细介绍你必会的高阶函数。
🌱 什么是高阶函数?
一句话:
高阶函数 = 可以把函数当作参数传入的函数。
例如:
(map add1 '(1 2 3))
这里 add1 是“被传入 map 的参数”,就像普通值一样,非常自然。
🔷 map —— 对每个元素做转换
map 就是“把函数应用到列表的每个元素上”。
基本示例
(map add1 '(1 2 3))
;; '(2 3 4)
使用匿名函数
(map (λ (x) (* x x)) '(1 2 3 4))
;; '(1 4 9 16)
多列表并行 map
(map + '(1 2 3) '(10 20 30))
;; '(11 22 33)
🔷 filter —— 挑出满足条件的元素
(filter even? '(1 2 3 4 5 6))
;; '(2 4 6)
字符串示例:
(filter (λ (s) (> (string-length s) 3))
'("hi" "hello" "abc" "rack"))
;; '("hello" "rack")
🔷 apply —— 把列表“展开”成函数参数
apply 用来执行“带可变参数”的函数非常好用。
求和
(apply + '(1 2 3 4))
;; 10
等价于:
(+ 1 2 3 4)
拼接多个列表
(apply append '((1 2) (3 4) (5 6)))
;; '(1 2 3 4 5 6)
动态参数(自动化脚本常用)
(define args '(1 2 3))
(apply format "~a ~a ~a" args)
;; "1 2 3"
🔷 foldl / foldr —— 把列表“压缩”为单个值
折叠是函数式编程的核心。
foldl(从左折叠)
(foldl + 0 '(1 2 3 4))
;; 10
其本质是:
(+ (+ (+ (+ 0 1) 2) 3) 4)
foldr(从右折叠)
常用于构造结构:
(foldr cons '() '(1 2 3))
;; '(1 2 3)
🔷 ormap / andmap —— 用逻辑判断整个列表
ormap
“只要一个满足条件,就返回 #t”
(ormap even? '(1 3 5 6))
;; #t
andmap
“所有元素都满足条件,才返回 #t”
(andmap number? '(1 2 3))
;; #t
🌟 为什么说“从循环到函数式”?
这是新手最常问的问题。
下面用最典型的例子说明。
✔ 传统命令式写法(像 C 语言)
(define (double-evens lst)
(define acc '())
(for ([x lst])
(when (even? x)
(set! acc (append acc (list (* x 2))))))
acc)
问题:
- 要维护
acc - 要写
set! append是 O(n),循环里 append 会变 O(n²)- 逻辑是“我怎么做”
✔ Racket / 函数式写法
(map (λ (x) (* x 2))
(filter even? lst))
优势:
- 不需要变量
- 不需要改变状态
- 不需要关心遍历顺序
- 写的就是“做什么”
✔ 为什么这个风格更好?
1. 更容易读
读起来像自然语言:
“过滤偶数,再翻倍。”
而不是:
“我声明 acc 并遍历每个 x,符合条件时 append...”
2. 更少 bug
没有:
- 变量越界
- append 位置错误
- 意外改变状态
- 遍历顺序写错
3. 更容易组合
高阶函数像积木:
(map square (map add1 (filter even? lst)))
功能组合清晰明确。
4. 更适合递归语言 Racket
Racket 的核心是:
- 列表处理
- 递归
- 函数组合
高阶函数和这些特性完美契合。
🌟 常见组合技巧
1. map + filter
(map add1 (filter even? '(1 2 3 4 5 6)))
;; '(3 5 7)
2. apply + map(矩阵处理常用)
(apply map max '((1 9 3)
(4 2 6)
(7 5 0)))
;; '(7 9 6)
3. foldl 模拟 map(理解 fold 的好方法)
(foldl (λ (x acc) (cons (* x x) acc))
'()
'(1 2 3 4))
;; '(16 9 4 1)
🏁 总结
通过这篇文章,你已经学会:
- 什么是高阶函数
- map / filter 用来处理列表
- apply 用来展开参数
- foldl / foldr 用来折叠数据
- andmap / ormap 用来做逻辑判断
- 为什么 Racket 强调“从循环到函数式”
- 函数式写法如何更安全、精简、可组合
这些高阶函数不仅能让你写出更优雅的 Racket 代码,也是走向:
- DSL(领域语言)
- 宏编程
- Web Server
- GUI 应用
- 游戏脚本
- 代码生成器
的基础能力。